﻿/**
 * @file dial.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "lv_watch.h"

#if USE_LV_WATCH_DIAL != 0

#include <stdio.h>
#include <stdlib.h>
#include "unzip.h"
#include "hal/dm/cJSON.h"
/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/
static void dial_clock_create(lv_obj_t * obj, watch_dial_type_t dial_type);
static char * dial_read_file(const char * file_name);
static void dial_remove_file(watch_dial_type_t dial_type, uint8_t ambient);
static lv_obj_t * g_clock_cont = NULL;

/**********************
 *  STATIC VARIABLES
 **********************/

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
***********************/
lv_obj_t * dial_create(lv_obj_t * tileview, lv_obj_t * ancient_tile, uint8_t index)
{
    lv_obj_t * tile;
    if(ancient_tile == NULL) {
        tile = launcher_tileview_add_tile(tileview, index);
        LV_ASSERT_MEM(tile);
        if(tile == NULL) return NULL;
    } else {
        //printf("dial_create: lv_tabview_clean ancient_tab=%p\n", ancient_tab);
        lv_obj_t * scrl = lv_page_get_scrl(tileview);
        lv_obj_clean(scrl);
        tile = ancient_tile;
    }

    watch_dial_type_t dial_type = dial_read_type(0);
    dial_clock_create(tile, dial_type);
    return tile;
}

static void dial_update_display(lv_obj_t * clock_cont)
{
    lv_clock_ext_t * clock_ext = (lv_clock_ext_t *)lv_obj_get_ext_attr(clock_cont);
    watch_dial_t * watch_dial = clock_ext->watch_dial;
    clock_ext->sec++;
    if(60  == clock_ext->sec) {
        clock_ext->sec = 0;
        clock_ext->min++;
        if(clock_ext->min % 10 == 0) {
            hal_rtc_t tm;  //sync local time every 10 min
            Hal_Rtc_Gettime(&tm);
            clock_ext->hour = tm.tm_hour;
            clock_ext->min  = tm.tm_min;
            clock_ext->sec  = tm.tm_sec;

#if DIAL_USE_CANVAS == 0
            dial_update_element(watch_dial, ELEMENT_TYPE_STEP);
            dial_update_element(watch_dial, ELEMENT_TYPE_CALORIE);
            dial_update_element(watch_dial, ELEMENT_TYPE_DISTANCE);//update every 10 min
#endif
        }
        if(60 == clock_ext->min) {
#if DIAL_USE_CANVAS == 0
            dial_update_element(watch_dial, ELEMENT_TYPE_DATE);
            dial_update_element(watch_dial, ELEMENT_TYPE_WEEK);//update every 60 min
#endif
        }
#if DIAL_USE_CANVAS == 0
        dial_update_element(watch_dial, ELEMENT_TYPE_AM_PM);
        dial_update_element(watch_dial, ELEMENT_TYPE_HEART_RATE);
        dial_update_element(watch_dial, ELEMENT_TYPE_WEATHER);
        dial_update_element(watch_dial, ELEMENT_TYPE_PRESSURE);
        dial_update_element(watch_dial, ELEMENT_TYPE_BLOOD_OXYGEN);
        dial_update_element(watch_dial, ELEMENT_TYPE_SLEEP_QUALITY);
        dial_update_element(watch_dial, ELEMENT_TYPE_BATTERY);//update every 1 min
#else
        dial_clean_canvas(false);
        uint8_t i = 0;
        for(i = ELEMENT_TYPE_BLOOD_OXYGEN; i > ELEMENT_TYPE_INVILID; i--) {
            dial_update_element(watch_dial, i);
        }

#endif

    }
#if DIAL_USE_CANVAS == 0
    dial_update_element(watch_dial, ELEMENT_TYPE_TIME_HAND);
    dial_update_element(watch_dial, ELEMENT_TYPE_TIME_DIGIT);//update every 1 sec
#else
    dial_update_second(clock_ext->sec, watch_dial);
#endif
}

static void dial_update_display_cb(lv_anim_t * ptr)
{
    if(ptr->var != NULL)
        dial_update_display(ptr->var);
}

void dial_clock_start_update(lv_obj_t * clock_cont)
{
    lv_anim_t a;
    lv_anim_path_t path;

    lv_anim_path_init(&path);
    lv_anim_path_set_cb(&path, lv_anim_path_step);
    lv_anim_init(&a);
    lv_anim_set_path(&a, &path);
    lv_anim_set_var(&a, clock_cont);
    lv_anim_set_time(&a, 0);
    lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
    lv_anim_set_repeat_delay(&a, 1000);
    lv_anim_set_start_cb(&a, dial_update_display_cb);

    lv_clock_ext_t * ext = lv_obj_get_ext_attr(clock_cont);

    //save clock time
    hal_rtc_t tm;
    Hal_Rtc_Gettime(&tm);

    ext->hour = tm.tm_hour;
    ext->min  = tm.tm_min;
    ext->sec  = tm.tm_sec;

    lv_anim_start(&a);
}

void dial_clock_stop_update(void)
{
    bool del = lv_anim_del((void *)g_clock_cont, NULL);
    if(del)
        printf("%s\n", __FUNCTION__);
}

void dial_clock_update_resume(lv_task_t * task)
{
    if(g_clock_cont) {
        if(lv_anim_get(g_clock_cont, NULL) == NULL) {
            printf("%s\n", __FUNCTION__);
            dial_clock_start_update(g_clock_cont);
        }
    }
}

void dial_clock_resume_task(void)
{
    if(launcher_tileview_act_is_dial()) {
        lv_task_t * update_resume_task = lv_task_create(dial_clock_update_resume, 100, LV_TASK_PRIO_HIGH, NULL);
        lv_task_set_repeat_count(update_resume_task, 1);
    }
}

void dial_ambient_update(void)
{
    lv_obj_t * ambient_obj = lv_watch_get_activity_obj(ACT_ID_DIAL_AMBIENT);

    if(NULL != ambient_obj) {
        printf("%s: update ambient\n", __FUNCTION__);
        lv_clock_ext_t * ext = lv_obj_get_ext_attr(lv_obj_get_child(ambient_obj, NULL));
        uint8_t i = 0;
#if DIAL_USE_CANVAS
        dial_clean_canvas(false);
#endif
        for(i = ELEMENT_TYPE_MAX - 2; i > ELEMENT_TYPE_INVILID; i--) {
            dial_update_element(ext->watch_dial, i);
        }
        Hal_Lcd_idle_refresh();
    }
}

void dial_clock_update_immediately(void)
{
    lv_obj_t * activity_obj = lv_watch_get_activity_obj(ACT_ID_LAUNCHER);
    if(NULL == activity_obj || NULL == g_clock_cont) return;
    printf("%s:\n", __FUNCTION__);
    //update clock time
    lv_clock_ext_t * ext = lv_obj_get_ext_attr(g_clock_cont);

    hal_rtc_t tm;  //sync local time every 10 min
    Hal_Rtc_Gettime(&tm);
    ext->hour = tm.tm_hour;
    ext->min  = tm.tm_min;
    ext->sec  = tm.tm_sec;

    uint8_t i = 0;
    for(i = ELEMENT_TYPE_MAX - 2 ; i > ELEMENT_TYPE_INVILID; i--) {
        dial_update_element(ext->watch_dial, i);
    }
    lv_refr_now(NULL);
}

lv_obj_t * dial_get_clock_cont(void)
{
    lv_obj_t * ambient_obj = lv_watch_get_activity_obj(ACT_ID_DIAL_AMBIENT);
    if(ambient_obj != NULL)
        return lv_obj_get_child(ambient_obj, NULL);
    else
        return g_clock_cont;
}

void dial_set_cont_null(void)
{
    g_clock_cont = NULL;
}

uint8_t dial_get_config_state(void)
{
    if(dial_get_clock_cont())
        return ((lv_clock_ext_t *)lv_obj_get_ext_attr(g_clock_cont))->configuring;
    else
        return 0;
}

static void dial_clock_create(lv_obj_t * obj, watch_dial_type_t dial_type)
{
    if(!obj) obj = lv_scr_act();

    lv_obj_t * clock_cont = lv_cont_create(obj, NULL);
    g_clock_cont = clock_cont;
    lv_obj_set_size(clock_cont, lv_obj_get_width(obj), lv_obj_get_height(obj));
    lv_obj_add_style(clock_cont, LV_CONT_PART_MAIN, &lv_style_transp_tight);
    lv_obj_set_click(clock_cont, false);
    lv_obj_align(clock_cont, NULL, LV_ALIGN_CENTER, 0, 0);

    lv_clock_ext_t * clock_ext = lv_obj_allocate_ext_attr(clock_cont, sizeof(lv_clock_ext_t));
    clock_ext->configuring = 0;
    watch_dial_t * watch_dial = lv_mem_alloc(sizeof(watch_dial_t));

    memset(watch_dial, 0, sizeof(watch_dial_t));

    char dsc_file[WATCH_MAX_PATH_LEN] = {0};
    dial_get_dsc_file(dial_type, 0, dsc_file);
    dial_parse(dsc_file, watch_dial);
    dial_init_act_element(watch_dial);

    clock_ext->watch_dial = watch_dial;
    if(dial_add_element(clock_cont, watch_dial)) {
        // update all element onetime first
        uint8_t i = 0;
        for(i = ELEMENT_TYPE_MAX - 2; i > ELEMENT_TYPE_INVILID; i--) {
            dial_update_element(watch_dial, i);
        }
        // start update task
        dial_clock_start_update(clock_cont);
    } else {
        printf("%s: load dial abort!!! delete it!!!", __FUNCTION__);
        dial_del_dial(watch_dial->name, 0);
    }
} /* dial_default_clock_create */

void dial_ambient_destroy(lv_obj_t * activity_obj)
{
    dial_destroy();
    lv_img_cache_invalidate_src(NULL);
#ifndef BUILD_IN_PC_SIMULATOR
    lv_obj_del(activity_obj);
#endif
    if(lv_watch_get_top_activity_obj() == lv_watch_get_activity_obj(ACT_ID_LAUNCHER)
            && launcher_tileview_act_is_dial())
        dial_clock_resume_task();
}

lv_obj_t * dial_ambeint_create(lv_obj_t * activity_obj)
{
    lv_watch_activity_ext_t activity_ext;
    memset(&activity_ext, 0, sizeof(lv_watch_activity_ext_t));
    activity_ext.actId = ACT_ID_DIAL_AMBIENT;
    activity_ext.create = dial_ambeint_create;
    activity_ext.prepare_destory = dial_ambient_destroy;
    activity_obj = lv_watch_creat_activity_obj(&activity_ext);
    if(activity_obj == NULL) return NULL;

    lv_obj_t * am_cont = lv_cont_create(activity_obj, NULL);
    lv_obj_set_size(am_cont, LV_HOR_RES, LV_VER_RES);
    lv_obj_set_style_local_bg_color(am_cont, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK);
    lv_obj_set_click(am_cont, false);
    lv_obj_align(am_cont, NULL, LV_ALIGN_CENTER, 0, 0);
    lv_clock_ext_t * am_clock_ext = lv_obj_allocate_ext_attr(am_cont, sizeof(lv_clock_ext_t));

    watch_dial_t * watch_ambient_dial = lv_mem_alloc(sizeof(watch_dial_t));
    memset(watch_ambient_dial, 0, sizeof(watch_dial_t));
    am_clock_ext->watch_dial = watch_ambient_dial;
    am_clock_ext->configuring = 0;

    char dsc_file[WATCH_MAX_PATH_LEN] = {0};
    uint8_t ambient_type = dial_read_type(1);
    dial_get_dsc_file(ambient_type, 1, dsc_file);
    dial_parse(dsc_file, watch_ambient_dial);
    dial_add_element(am_cont, watch_ambient_dial);
    // update all element onetime first
    uint8_t i = 0;
    for(i = ELEMENT_TYPE_TIME_DIGIT; i < ELEMENT_TYPE_MAX - 1; i++) {
        dial_update_element(watch_ambient_dial, i);
    }
    return am_cont;
}

bool dial_switch_to_ambient(void)
{
    if(lv_watch_get_activity_obj(ACT_ID_POWER_OFF_CHARGING)) {
        printf("%s poweroff charging\n", __FUNCTION__);
        return false;
    }
#if USE_LV_WATCH_DIAL_AMBIENT != 0
    uint8_t psm_type = dial_read_type(1);
    printf("%s: psm_type = %d\n", __FUNCTION__, psm_type);
    if(psm_type != 0) {
        if(lv_watch_get_activity_obj(ACT_ID_DIAL_AMBIENT) == NULL) {
            dial_clock_stop_update();
#if DIAL_USE_CANVAS
            dial_clean_canvas(false);
            lv_img_cache_invalidate_src(NULL);
#endif
            dial_ambeint_create(NULL);
            lv_refr_now(NULL);
        }
        return true;
    } else {
        printf("%s: no psm dial\n", __FUNCTION__);
#if DIAL_USE_CANVAS
        if(!watch_get_sleep_lcd_only())
            dial_clean_canvas(false);
#endif
        return false;
    }
#else
#if DIAL_USE_CANVAS
    if(!watch_get_sleep_lcd_only())
        dial_clean_canvas(false);
#endif
    return false;
#endif
}

bool dial_is_ttf_using(char * ttf_file)
{
    lv_obj_t * ambient_obj = lv_watch_get_activity_obj(ACT_ID_DIAL_AMBIENT);
    if(ambient_obj) {
        lv_clock_ext_t * ext = lv_obj_get_ext_attr(g_clock_cont);
        watch_element_t * element;
        watch_element_base_t ** element_base_node;
        watch_element_label_dsc_t * element_label;
        _LV_LL_READ(ext->watch_dial->element_ll, element) {
            _LV_LL_READ(element->element_dsc_ll, element_base_node) {
                watch_element_base_t * element_base = (watch_element_base_t *)*element_base_node;
                _LV_LL_READ(element_base->label_ll, element_label) {
                    if(strncmp(element_label->ttf_file, ttf_file, strlen(ttf_file)) == 0) {
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

void dial_free_element(watch_dial_t * dial)
{
    watch_element_t * element;
    watch_element_base_t ** element_base_node;
    watch_element_img_dsc_t * element_img;
    watch_element_label_dsc_t * element_label;
    _LV_LL_READ(dial->element_ll, element) {
        _LV_LL_READ(element->element_dsc_ll, element_base_node) {
            watch_element_base_t * element_base = (watch_element_base_t *)*element_base_node;
            _LV_LL_READ(element_base->img_ll, element_img) {
                _lv_ll_clear(&element_img->subimg->sub_img_ll);
                lv_mem_free(element_img->subimg);
            }
            _lv_ll_clear(&element_base->img_ll);

            _LV_LL_READ(element_base->label_ll, element_label) {
                if(element_label->text)
                    lv_mem_free(element_label->text);
#ifdef USE_FONT_TTF
                if(element_label->label_font != NULL) {
                    if(strlen(element_label->ttf_file) > 0 && !dial_is_ttf_using(element_label->ttf_file)) {
                        lv_freetype_font_destroy(element_label->label_font);
                    }
                    lv_mem_free(element_label->label_font->user_data);
                    lv_mem_free(element_label->label_font);
                    element_label->label_font = NULL;
                }
#endif
            }
            _lv_ll_clear(&element_base->label_ll);
            lv_mem_free(element_base);
        }
        _lv_ll_clear(&element->element_dsc_ll);
    }
    _lv_ll_clear(&dial->element_ll);
}

void dial_destroy(void)
{
    dial_reset_bg_index();
    lv_clock_ext_t * ext = lv_obj_get_ext_attr(dial_get_clock_cont());
    if(ext->watch_dial) {
        dial_free_element(ext->watch_dial);
        lv_mem_free(ext->watch_dial);
        ext->watch_dial = NULL;
    }
#if DIAL_USE_CANVAS != 0
    if(ext->canvas) {
        lv_canvas_ext_t * canvas_ext = lv_obj_get_ext_attr(ext->canvas);
        lv_mem_free(canvas_ext->dsc.data);
        lv_obj_del(ext->canvas);
        ext->canvas = NULL;
    }
#endif
}

void dial_poweroff(void)
{
    dial_destroy();
    lv_obj_del(g_clock_cont);
    g_clock_cont = NULL;
}

watch_dial_type_t dial_read_type(uint8_t ambient)
{
    watch_dial_type_t cur_type = 0;
    if(ambient)
        UI_NV_Read_Req(NV_SECTION_UI_LAUNCHER, sizeof(uint8_t), sizeof(uint8_t), &cur_type);
    else {
        UI_NV_Read_Req(NV_SECTION_UI_LAUNCHER, 0, sizeof(uint8_t), &cur_type);
        watch_set_dial_type(cur_type);
    }
    return cur_type;
}

void dial_write_type(watch_dial_type_t cur_type, uint8_t ambient)
{
    if(ambient)
        UI_NV_Write_Req(NV_SECTION_UI_LAUNCHER, sizeof(uint8_t), sizeof(uint8_t), &cur_type);
    else
        UI_NV_Write_Req(NV_SECTION_UI_LAUNCHER, 0, sizeof(uint8_t), &cur_type);
}

uint8_t dial_list_get_dial_cnt(char * list_path)
{
    lv_fs_file_t list_file;

    uint8_t res = lv_fs_open(&list_file, list_path, LV_FS_MODE_RD);

    if(LV_FS_RES_OK != res) {
        printf("%s dail list file open fail: %s\n", __FUNCTION__, list_path);
        return 0;
    }

    uint32_t rn, size, i = 0;
    uint8_t line = 0;
    char * temp_buf = NULL;
    lv_fs_size(&list_file, &size);
    temp_buf = lv_mem_alloc(size + 1);
    memset(temp_buf, 0, size + 1);
    lv_fs_read(&list_file, temp_buf, size, &rn);
    if(rn == 0) {
        printf("%s read dial dsc file fail list_path = %s\n", __FUNCTION__, list_path);
        lv_mem_free(temp_buf);
        lv_fs_close(&list_file);
        return 0;
    }

    for(i = 0; i < rn; i++) {
        if(temp_buf[i] == '\n' || i == rn - 1) {
            line++;
        }
    }

    lv_mem_free(temp_buf);
    lv_fs_close(&list_file);
    printf("%s dial_cnt = %d\n", __FUNCTION__, line / 2);
    return line / 2;
}

void dial_get_dsc_file(uint8_t dial_type, uint8_t psm, char * dsc_file_name)
{
    lv_fs_file_t dsc_list_file;
    char * list_path;
    uint8_t buildin_cnt;
    uint8_t ext_cnt;
    if(psm) {
        buildin_cnt = dial_list_get_dial_cnt(WATCH_DIAL_BUILDIN_AM_LIST_FILE);
        ext_cnt = dial_list_get_dial_cnt(WATCH_DIAL_EXT_AM_LIST_FILE);
        if(buildin_cnt <= dial_type) {
            dial_type -= buildin_cnt;
            list_path = WATCH_DIAL_EXT_AM_LIST_FILE;
            if(ext_cnt <= dial_type) {
                if(ext_cnt > 0)
                    dial_type = ext_cnt - 1;
                else {
                    dial_type = 1;
                    list_path = WATCH_DIAL_BUILDIN_AM_LIST_FILE;
                }
            }
        } else {
            list_path = WATCH_DIAL_BUILDIN_AM_LIST_FILE;
        }
    } else {
        buildin_cnt = dial_list_get_dial_cnt(WATCH_DIAL_BUILDIN_LIST_FILE);
        ext_cnt = dial_list_get_dial_cnt(WATCH_DIAL_EXT_LIST_FILE);
        if(buildin_cnt <= dial_type) {
            list_path = WATCH_DIAL_EXT_LIST_FILE;
            dial_type -= buildin_cnt;
            if(ext_cnt <= dial_type) {
                if(ext_cnt > 0)
                    dial_type = ext_cnt - 1;
                else {
                    dial_type = 0;
                    list_path = WATCH_DIAL_BUILDIN_LIST_FILE;
                }
            }
        } else {
            list_path = WATCH_DIAL_BUILDIN_LIST_FILE;
        }
    }

    printf("%s: list file %s\n", __FUNCTION__, list_path);
    uint8_t res = lv_fs_open(&dsc_list_file, list_path, LV_FS_MODE_RD);

    if(LV_FS_RES_OK != res) {
        printf("%s there is no configured watch dial\n", __FUNCTION__);
        return;
    }

    uint32_t rn, size, i, j = 0;
    uint8_t temp_type = 0;
    char * temp_buf = NULL;
    lv_fs_size(&dsc_list_file, &size);
    temp_buf = lv_mem_alloc(size + 1);
    memset(temp_buf, 0, size + 1);
    lv_fs_read(&dsc_list_file, temp_buf, size, &rn);
    for(i = 0; i < rn; i++) {
        if(temp_buf[i] == '\n' || i == rn - 1) {
            if(temp_type++ == dial_type * 2 + 1) {
                if(temp_buf[i] != '\n' && i == rn - 1) {
                    i++;
                    j++;
                }
                memcpy(dsc_file_name, &temp_buf[i - j], j);
                if(dsc_file_name[j - 1] == '\r') dsc_file_name[j - 1] = 0;
                break;
            }
            j = 0;
        } else {
            j++;
        }
    }

    lv_mem_free(temp_buf);
    lv_fs_close(&dsc_list_file);
}

void dial_add_new_dial(char * zip_path)
{
    lv_fs_file_t dsc_list_file;
    uint32_t wn = 0;
    uint8_t ext_cnt = 0;
    uint8_t res;
    watch_dial_conf_path_t * conf_path = NULL;
    cJSON * json_root = NULL;
    char * list_file;
    char * name = NULL;

    if(NULL == zip_path) return;

    printf("%s: zip_path:%s\n", __FUNCTION__, zip_path);

    conf_path = dial_unzip(zip_path);
    if(NULL == conf_path) {
        printf("%s: get json file failed from zip_path %s\n", __FUNCTION__, zip_path);
        return;
    }
    lv_fs_remove(zip_path);

    for(uint8_t i = 0; i < conf_path->json_file_cnt; i++) {

        char * content = dial_read_file(conf_path->json_file[i]);
        if(content) {
            json_root = cJSON_Parse(content);
            lv_mem_free(content);
        }
        if(NULL == json_root) {
            printf("%s: json parse error\n", __FUNCTION__);
            return;
        }

        name = cJSON_GetStringValue(cJSON_GetObjectItem(json_root, "name"));
        uint8_t config = cJSON_GetIntValue(cJSON_GetObjectItem(json_root, "configurable"));
        printf("%s: config = %d, name=%s\n", __FUNCTION__, config, name);
        if(config == 2) list_file = WATCH_DIAL_EXT_AM_LIST_FILE;
        else list_file = WATCH_DIAL_EXT_LIST_FILE;

        ext_cnt = dial_list_get_dial_cnt(list_file);
        printf("%s cnt %d\n", __FUNCTION__, ext_cnt);
        if(ext_cnt != 0) {
            res = lv_fs_open(&dsc_list_file, list_file, LV_FS_MODE_WR | LV_FS_MODE_APPEND);
        } else {
            res = lv_fs_open(&dsc_list_file, list_file, LV_FS_MODE_WR);
        }

        if(LV_FS_RES_OK != res) {
            printf("%s open list file fail\n", __FUNCTION__);
            lv_mem_free(conf_path);
            cJSON_Delete(json_root);
            return;
        }

        lv_fs_write(&dsc_list_file, name, strlen(name), &wn);
        if(wn == 0) {
            printf("%s write name file fail\n", __FUNCTION__);
            goto err;
        }

        lv_fs_write(&dsc_list_file, "\n", 1, &wn);
        if(wn == 0) {
            printf("%s write list file fail\n", __FUNCTION__);
            goto err;
        }

        lv_fs_write(&dsc_list_file, conf_path->json_file[i], strlen(conf_path->json_file[i]), &wn);
        if(wn == 0) {
            printf("%s write conf_path file fail\n", __FUNCTION__);
            goto err;
        }

        lv_fs_write(&dsc_list_file, "\n", 1, &wn);
        if(wn == 0) {
            printf("%s write list file fail 1\n", __FUNCTION__);
            goto err;
        }

        lv_fs_close(&dsc_list_file);
        cJSON_Delete(json_root);

        if(lv_watch_get_activity_obj(ACT_ID_LAUNCHER) && g_clock_cont && config < 2) {
            uint8_t dial_type = dial_list_get_dial_cnt(WATCH_DIAL_BUILDIN_LIST_FILE) + ext_cnt;
            dial_destroy();
            lv_obj_t * par = lv_obj_get_parent(g_clock_cont);
            lv_obj_clean(par);
            dial_write_type(dial_type, 0);
            dial_clock_create(par, dial_type);
        }
    }
    lv_mem_free(conf_path);
    return;

err:
    lv_fs_close(&dsc_list_file);
    lv_mem_free(conf_path);
    cJSON_Delete(json_root);
    return;
}

void dial_remove_dial_from_list(watch_dial_type_t dial_type, uint8_t ambient)
{
    uint32_t rn, wn, size, i, j = 0;
    lv_fs_file_t dsc_list_file;
    uint8_t temp_type = 0;
    char * temp_buf = NULL;
    uint8_t res = 0;

    if(ambient) res = lv_fs_open(&dsc_list_file, WATCH_DIAL_EXT_AM_LIST_FILE, LV_FS_MODE_RD);
    else res = lv_fs_open(&dsc_list_file, WATCH_DIAL_EXT_LIST_FILE, LV_FS_MODE_RD);
    if(LV_FS_RES_OK != res) {
        printf("%s open list file fail\n", __FUNCTION__);
        return;
    }

    lv_fs_size(&dsc_list_file, &size);
    temp_buf = lv_mem_alloc(size + 1);
    temp_buf[size] = 0;
    lv_fs_read(&dsc_list_file, temp_buf, size, &rn);
    lv_fs_close(&dsc_list_file);

    //clear file and update
    if(ambient) res = lv_fs_open(&dsc_list_file, WATCH_DIAL_EXT_AM_LIST_FILE, LV_FS_MODE_WR);
    else res = lv_fs_open(&dsc_list_file, WATCH_DIAL_EXT_LIST_FILE, LV_FS_MODE_WR);

    if(LV_FS_RES_OK != res) {
        printf("%s open list file fail 1\n", __FUNCTION__);
        lv_mem_free(temp_buf);
        return;
    }

    for(i = 0; i < rn; i++) {
        if(temp_buf[i] == '\n' || i == rn - 1) {
            printf("%s : temp_type = %d dial_type = %d\n", __FUNCTION__, temp_type, dial_type);
            if(dial_type == 0) {
                if(temp_type == 1) {
                    if(i == rn - 1) {
                        j++;
                    }
                    if(i < rn - 1)
                        lv_fs_write(&dsc_list_file, &temp_buf[i + 1], rn - i - 1, &wn);
                    break;
                }
            } else {
                if(temp_type == 2 * dial_type) {
                    if(i == rn - 1) {
                        j++;
                    }
                    lv_fs_write(&dsc_list_file, temp_buf, i - j, &wn);
                    i++;
                } else if(temp_type == 2 * dial_type + 1) {
                    if(i == rn - 1) {
                        j++;
                    }
                    if(i < rn - 1)
                        lv_fs_write(&dsc_list_file, &temp_buf[i + 1], rn - i - 1, &wn);
                    break;
                }
            }
            temp_type++;
            j = 0;
        } else {
            j++;
        }
    }

    lv_mem_free(temp_buf);
    lv_fs_close(&dsc_list_file);
}

static int8_t dial_get_type_by_name(char * name, uint8_t ambient)
{
    lv_fs_file_t dsc_list_file;
    uint32_t rn, size, i, j = 0;
    char * temp_buf = NULL;
    char temp_name[20];
    uint8_t line = 0;
    bool find = false;
    uint8_t res;
    if(ambient)
        res = lv_fs_open(&dsc_list_file, WATCH_DIAL_EXT_AM_LIST_FILE, LV_FS_MODE_RD);
    else
        res = lv_fs_open(&dsc_list_file, WATCH_DIAL_EXT_LIST_FILE, LV_FS_MODE_RD);
    if(LV_FS_RES_OK != res) {
        printf("%s: open list file fail\n", __FUNCTION__);
        return -1;
    }

    lv_fs_size(&dsc_list_file, &size);
    temp_buf = lv_mem_alloc(size + 1);
    temp_buf[size] = 0;
    lv_fs_read(&dsc_list_file, temp_buf, size, &rn);
    lv_fs_close(&dsc_list_file);

    for(i = 0; i < rn; i++) {
        if(temp_buf[i] == '\n') {
            if(line % 2 == 0) {
                if(temp_buf[i] != '\n' && i == rn - 1) {
                    i++;
                    j++;
                }
                memset(temp_name, 0, 20);
                memcpy(temp_name, &temp_buf[i - j], j);
                printf("%s: temp_name = %s line = %d\n", __FUNCTION__, temp_name, line);
                if(memcmp(temp_name, name, strlen(name)) == 0) {
                    find = true;
                    break;
                }
            }
            line++;
            j = 0;
        } else {
            j++;
        }
    }

    lv_mem_free(temp_buf);
    if(find) {
        printf("%s: type = %d\n", __FUNCTION__, line / 2);
        return line / 2;
    } else {
        printf("%s: can't find the name \n", __FUNCTION__);
        return -1;
    }
}

//false means fail
bool dial_del_dial(char * name, uint8_t ambient)
{
    uint8_t buildin_cnt = 0;
    uint8_t ext_cnt = 0;
    uint8_t dial_type =  0;
    int8_t ext_type = dial_get_type_by_name(name, ambient);
    if(ambient == 0) {
        buildin_cnt = dial_list_get_dial_cnt(WATCH_DIAL_BUILDIN_LIST_FILE);
        ext_cnt = dial_list_get_dial_cnt(WATCH_DIAL_EXT_LIST_FILE);
    } else {
        buildin_cnt = dial_list_get_dial_cnt(WATCH_DIAL_BUILDIN_AM_LIST_FILE);
        ext_cnt = dial_list_get_dial_cnt(WATCH_DIAL_EXT_AM_LIST_FILE);
    }
    if(ext_type < 0 || ext_type >= ext_cnt) {
        printf("%s wrong dial_type %d, buildin_cnt:%d, ext_cnt:%d\n",
               __FUNCTION__, dial_type, buildin_cnt, ext_cnt);
        return false;
    }
    dial_type = ext_type + buildin_cnt;

    watch_dial_type_t cur_type = dial_read_type(ambient);
    if(dial_type == cur_type) cur_type = 0;
    else if(dial_type < cur_type) cur_type--;
    dial_write_type(cur_type, ambient);

    if(ambient == 0) {
        if(lv_watch_get_activity_obj(ACT_ID_LAUNCHER) && g_clock_cont) {
            dial_destroy();
            lv_obj_t * par = lv_obj_get_parent(g_clock_cont);
            lv_obj_clean(par);
            dial_clock_create(par, cur_type);
        }
    }
    dial_remove_file(ext_type, ambient);
    dial_remove_dial_from_list(ext_type, ambient);

    return true;
}

watch_dial_conf_path_t * dial_unzip(const char * zip_file_name)
{
    int ret;
    unz_file_info file_info;
    char file_name[DIAL_PATH_MAX] = {0};
    char extra_name[DIAL_PATH_MAX] = {0};
    char comm_name[DIAL_PATH_MAX] = {0};
    char out_full_name[DIAL_PATH_MAX] = {0};
    lv_fs_file_t out_file;
    void * read_buffer = NULL;
    const char * to_path = "C:/";
    watch_dial_conf_path_t * conf_path = NULL;
    uint8_t json_file_cnt = 0;

    printf("%s: zip_file_name = %s\n", __FUNCTION__, zip_file_name);
    if('C' != zip_file_name[0]) {
        return NULL;
    }
    memset(&out_file, 0, sizeof(lv_fs_file_t));

    //open zip file
    unzFile unzfile = unzOpen(zip_file_name);
    if(NULL == unzfile) {
        printf("%s: unzOpen fail\n", __FUNCTION__);
        goto exit;
    }

    //get global info
    unz_global_info global_info;
    ret = unzGetGlobalInfo(unzfile, &global_info);
    if(ret != UNZ_OK) {
        printf("%s: unzGetGlobalInfo fail, ret=%d\n", __FUNCTION__, ret);
        goto exit;
    }
    printf("%s: global_info.number_entry = %lu\n", __FUNCTION__, global_info.number_entry);

    conf_path = lv_mem_alloc(sizeof(watch_dial_conf_path_t));
    memset(conf_path, 0, sizeof(watch_dial_conf_path_t));

    //parse zip file
    for(int i = 0; i < global_info.number_entry; i++) {
        //get current file info
        ret = unzGetCurrentFileInfo(unzfile, &file_info, file_name, DIAL_PATH_MAX, extra_name, DIAL_PATH_MAX, comm_name, DIAL_PATH_MAX);
        if(ret != UNZ_OK) {
            goto exit;
        }
        printf("%s: file_name = %s\n", __FUNCTION__, file_name);

        if(DIAL_FILE_DIR == file_info.external_fa) {
            //mkdir is not supported
        } else {
            //open current file
            ret = unzOpenCurrentFile(unzfile);
            if(ret != UNZ_OK) {
                printf("%s: unzOpenCurrentFile fail!\n", __FUNCTION__);
                goto exit;
            }

            //create out_file
            memset(out_full_name, 0, DIAL_PATH_MAX);
            strncpy(out_full_name, to_path, strlen(to_path));
            strcat(out_full_name, file_name);

            memset(&out_file, 0, sizeof(lv_fs_file_t));
            ret = lv_fs_open(&out_file, out_full_name, LV_FS_MODE_WR);
            if(LV_FS_RES_OK != ret) {
                printf("%s: lv_fs_open fail!\n", __FUNCTION__);
                goto exit;
            }
            lv_fs_close(&out_file);

            memset(&out_file, 0, sizeof(lv_fs_file_t));
            ret = lv_fs_open(&out_file, out_full_name, LV_FS_MODE_RD | LV_FS_MODE_APPEND);
            if(LV_FS_RES_OK != ret) {
                printf("%s: second lv_fs_open fail!\n", __FUNCTION__);
                goto exit;
            }

            if(!strcmp("json", lv_fs_get_ext(out_full_name)) && json_file_cnt < DIAL_MAX_JSON_FILE_CNT) {
                memcpy(conf_path->json_file[json_file_cnt], out_full_name, strlen(out_full_name) + 1);
                printf("%s: json_file_name[%d] = %s\n", __FUNCTION__, json_file_cnt, conf_path->json_file[json_file_cnt]);
                json_file_cnt++;
            }

            //read current file
            uint32_t pos = 0;
            uint32_t bw = 0;
            uint32_t buffer_size = file_info.uncompressed_size;;
            read_buffer = lv_mem_alloc(buffer_size);

            while(1) {
                memset(read_buffer, 0, buffer_size);
                int read_size = 0;

                read_size = unzReadCurrentFile(unzfile, read_buffer, buffer_size);

                if(read_size < 0) {     //read fail
                    unzCloseCurrentFile(unzfile);
                    printf("%s: unzReadCurrentFile fail!\n", __FUNCTION__);
                    goto exit;
                } else if(read_size == 0) {   //read complete
                    unzCloseCurrentFile(unzfile);
                    lv_fs_close(&out_file);
                    break;
                } else {  //write to out_file
                    lv_fs_seek(&out_file, pos);
                    ret = lv_fs_write(&out_file, read_buffer, read_size, &bw);
                    if(LV_FS_RES_OK != ret) {
                        printf("%s: lv_fs_write failed!\n", __FUNCTION__);
                        goto exit;
                    }
                    pos += read_size;
                }
            }

            lv_mem_free(read_buffer);
            read_buffer = NULL;
        }

        unzGoToNextFile(unzfile);
    }
    conf_path->json_file_cnt = json_file_cnt;

    //close zip file
    if(unzfile) {
        unzClose(unzfile);
    }

    if(out_file.file_d) {
        lv_fs_close(&out_file);
    }

    if(read_buffer) {
        lv_mem_free(read_buffer);
    }

    return conf_path;

exit:

    //close zip file
    if(unzfile) {
        unzClose(unzfile);
    }

    if(out_file.file_d) {
        lv_fs_close(&out_file);
    }

    if(read_buffer) {
        lv_mem_free(read_buffer);
    }

    if(conf_path) {
        lv_mem_free(conf_path);
    }

    return NULL;
}

static char * dial_read_file(const char * file_name)
{
    lv_fs_file_t file;
    uint32_t size = 0;
    uint32_t br = 0;
    char * content = NULL;

    if(NULL == file_name) {
        printf("%s: file_name is NULL\n", __FUNCTION__);
        return NULL;
    }

    uint8_t res = lv_fs_open(&file, file_name, LV_FS_MODE_RD);
    if(LV_FS_RES_OK != res) {
        printf("%s: %s does not exist\n", file_name, __FUNCTION__);
        return NULL;
    }

    lv_fs_size(&file, &size);
    content = lv_mem_alloc(size + 1);
    memset(content, 0, size + 1);
    lv_fs_read(&file, content, size, &br);
    lv_fs_close(&file);

    return content;
}

static void dial_remove_file(watch_dial_type_t dial_type, uint8_t ambient)
{
    lv_fs_file_t dsc_list_file;
    uint32_t rn, size, i, j = 0;
    uint8_t line = 0;
    uint8_t begin = 0;
    uint8_t end = 0;
    cJSON * json_root = NULL;
    char * path = NULL;
    char * temp_buf = NULL;
    char cfg_file_name[DIAL_PATH_MAX];
    uint8_t res;
    memset(cfg_file_name, 0, DIAL_PATH_MAX);

    //read ext list file
    line = dial_type * 2 + 1;

    if(ambient)
        res = lv_fs_open(&dsc_list_file, WATCH_DIAL_EXT_AM_LIST_FILE, LV_FS_MODE_RD);
    else
        res = lv_fs_open(&dsc_list_file, WATCH_DIAL_EXT_LIST_FILE, LV_FS_MODE_RD);
    if(LV_FS_RES_OK != res) {
        printf("%s: open list file fail\n", __FUNCTION__);
        return;
    }

    lv_fs_size(&dsc_list_file, &size);
    temp_buf = lv_mem_alloc(size + 1);
    temp_buf[size] = 0;
    lv_fs_read(&dsc_list_file, temp_buf, size, &rn);
    lv_fs_close(&dsc_list_file);

    //get json file name
    for(i = 0; i < rn; i++) {
        if(temp_buf[i] == '\n') {
            if(j++ == line) {
                end = i - 1;
                break;
            } else {
                begin = i + 1;
            }
        }
    }

    if(end > 0) {
        memcpy(cfg_file_name, temp_buf + begin, end - begin + 1);
        printf("%s: cfg_file_name=%s\n", __FUNCTION__, cfg_file_name);
    }
    lv_mem_free(temp_buf);

    //parse json file
    char * content = dial_read_file(cfg_file_name);
    if(content) {
        json_root = cJSON_Parse(content);
        lv_mem_free(content);
    }

    if(NULL == json_root) {
        printf("%s: json parse error\n", __FUNCTION__);
        return;
    }

    //remove file
    path = cJSON_GetStringValue(cJSON_GetObjectItem(json_root, "preview_img"));
    lv_fs_remove(path);
    printf("%s: remove %s\n", __FUNCTION__, path);

    path = cJSON_GetStringValue(cJSON_GetObjectItem(json_root, "bg_img"));
    lv_fs_remove(path);
    printf("%s: remove %s\n", __FUNCTION__, path);

    //element ll
    cJSON * json_element_ll = cJSON_GetObjectItem(json_root, "element_ll");
    cJSON * json_element = NULL;
    cJSON_ArrayForEach(json_element, json_element_ll) {
        //element dsc ll
        cJSON * json_element_dsc_ll = cJSON_GetObjectItem(json_element, "element_dsc_ll");
        cJSON * json_element_dsc = NULL;
        cJSON_ArrayForEach(json_element_dsc, json_element_dsc_ll) {
            path = cJSON_GetStringValue(cJSON_GetObjectItem(json_element_dsc, "element_pre_img"));
            lv_fs_remove(path);
            printf("%s: remove %s\n", __FUNCTION__, path);

            //img ll
            cJSON * json_img_ll = cJSON_GetObjectItem(json_element_dsc, "img_ll");
            cJSON * json_img = NULL;
            cJSON_ArrayForEach(json_img, json_img_ll) {
                //sub img ll
                cJSON * json_sub_img_ll = cJSON_GetObjectItem(json_img, "sub_img_ll");
                cJSON * json_sub_img = NULL;
                cJSON_ArrayForEach(json_sub_img, json_sub_img_ll) {
                    path = cJSON_GetStringValue(cJSON_GetObjectItem(json_sub_img, "sub_img"));
                    lv_fs_remove(path);
                    printf("%s: remove %s\n", __FUNCTION__, path);
                }
            }

            //label ll
            cJSON * json_label_ll = cJSON_GetObjectItem(json_element_dsc, "label_ll");
            cJSON * json_label = NULL;
            cJSON_ArrayForEach(json_label, json_label_ll) {
                path = cJSON_GetStringValue(cJSON_GetObjectItem(json_label, "ttf_file"));
                if(path) {
                    lv_fs_remove(path);
                    printf("%s: remove %s\n", __FUNCTION__, path);
                }
            }
        }
    }

    lv_fs_remove(cfg_file_name);
    printf("%s: remove %s\n", __FUNCTION__, cfg_file_name);

    cJSON_Delete(json_root);
}

static void dial_print(watch_dial_t * dial)
{
#if DIAL_DEBUG != 0
    if(!dial)  return;

    printf("%s: %s, %s, %s, %d\n", __FUNCTION__,
           dial->name,
           dial->preview_img,
           dial->bg_img,
           dial->configurable);

    //element
    watch_element_t * node_element = (watch_element_t *)_lv_ll_get_head(&dial->element_ll);
    while(node_element) {
        printf("------ element: (%d, %d), %d, %d, %d ------\n",
               node_element->element_pos.x,
               node_element->element_pos.y,
               node_element->element_cnt,
               node_element->act_type,
               node_element->layer);

        //element dsc
        watch_element_base_t ** node_element_dsc = (watch_element_base_t **)_lv_ll_get_head(&node_element->element_dsc_ll);
        while(node_element_dsc) {
            watch_element_base_t * element_dsc = *node_element_dsc;
            if(element_dsc) {
                printf("*** element_dsc: %d, %s, %s, %d, %d\n",
                       element_dsc->element_type,
                       element_dsc->element_name,
                       element_dsc->element_pre_img,
                       element_dsc->img_cnt,
                       element_dsc->label_cnt);

                //img dsc
                watch_element_img_dsc_t * node_img_dsc = (watch_element_img_dsc_t *)_lv_ll_get_head(&element_dsc->img_ll);
                while(node_img_dsc) {
                    printf("img_dsc: %d, (%d, %d)\n",
                           node_img_dsc->img_type,
                           node_img_dsc->img_pos.x,
                           node_img_dsc->img_pos.y);

                    //sub img dsc
                    watch_element_subimg_dsc_t * subimg = node_img_dsc->subimg;
                    if(subimg) {
                        printf("subimg_dsc: %d\n", subimg->sub_img_cnt);

                        char * node_subimg_dsc = (char *)_lv_ll_get_head(&subimg->sub_img_ll);
                        while(node_subimg_dsc) {
                            printf("subimg_dsc: %s\n", node_subimg_dsc);
                            node_subimg_dsc = (char *)_lv_ll_get_next(&subimg->sub_img_ll, node_subimg_dsc);
                        }
                    }

                    node_img_dsc = (watch_element_img_dsc_t *)_lv_ll_get_next(&element_dsc->img_ll, node_img_dsc);
                }

                //label dsc
                watch_element_label_dsc_t * node_label_dsc = (watch_element_label_dsc_t *)_lv_ll_get_head(&element_dsc->label_ll);
                while(node_label_dsc) {
                    printf("label_dsc: %d, %s, %d, (%d, %d), %d\n",
                           node_label_dsc->label_type,
                           node_label_dsc->text ? node_label_dsc->text : "(null)",
                           node_label_dsc->text_color.full,
                           node_label_dsc->text_pos.x,
                           node_label_dsc->text_pos.y,
                           node_label_dsc->font_size);

                    node_label_dsc = (watch_element_label_dsc_t *)_lv_ll_get_next(&element_dsc->label_ll, node_label_dsc);
                }

                //bar dsc
                watch_element_bar_dsc_t * bar_dsc = &element_dsc->bar_dsc;
                printf("bar_dsc: %d, (%d, %d), %d, %d, %d\n",
                       bar_dsc->bar_type,
                       bar_dsc->bar_pos.x,
                       bar_dsc->bar_pos.y,
                       bar_dsc->bar_color.full,
                       bar_dsc->bar_w,
                       bar_dsc->bar_h);
            }
            node_element_dsc = (watch_element_base_t **)_lv_ll_get_next(&node_element->element_dsc_ll, node_element_dsc);
        }

        node_element = (watch_element_t *)_lv_ll_get_next(&dial->element_ll, node_element);
    }
#endif
}

void dial_parse(const char * cfg_file_name, watch_dial_t * dial)
{
    cJSON * json_root = NULL;
    cJSON * json_item = NULL;
    char * valuestring = NULL;

    if(NULL == dial) {
        return;
    }

    char * content = dial_read_file(cfg_file_name);
    if(content) {
        json_root = cJSON_Parse(content);
        lv_mem_free(content);
    }

    if(NULL == json_root) {
        printf("%s: json parse error\n", __FUNCTION__);
        return;
    } else {
        //printf("%s\r\n",cJSON_Print(json_root));
    }
    memset(dial, 0, sizeof(watch_dial_t));

    valuestring = cJSON_GetStringValue(cJSON_GetObjectItem(json_root, "name"));
    if(valuestring) strncpy(dial->name, valuestring, WATCH_MAX_DIAL_NAME_LEN);

    valuestring = cJSON_GetStringValue(cJSON_GetObjectItem(json_root, "preview_img"));
    if(valuestring) strncpy(dial->preview_img, valuestring, WATCH_MAX_PATH_LEN);

    valuestring = cJSON_GetStringValue(cJSON_GetObjectItem(json_root, "bg_img"));
    if(valuestring) strncpy(dial->bg_img, valuestring, WATCH_MAX_PATH_LEN);

    dial->configurable = cJSON_GetIntValue(cJSON_GetObjectItem(json_root, "configurable"));

    //element ll
    _lv_ll_init(&dial->element_ll, sizeof(watch_element_t));

    cJSON * json_element_ll = cJSON_GetObjectItem(json_root, "element_ll");
    cJSON * json_element = NULL;
    cJSON_ArrayForEach(json_element, json_element_ll) {
        watch_element_t * dial_element = (watch_element_t *)_lv_ll_ins_tail(&dial->element_ll);
        memset(dial_element, 0, sizeof(watch_element_t));

        json_item = cJSON_GetObjectItem(json_element, "element_pos");
        dial_element->element_pos.x = cJSON_GetIntValue(cJSON_GetObjectItem(json_item, "x"));
        dial_element->element_pos.y = cJSON_GetIntValue(cJSON_GetObjectItem(json_item, "y"));
        dial_element->element_cnt = cJSON_GetIntValue(cJSON_GetObjectItem(json_element, "element_cnt"));
        dial_element->act_type = cJSON_GetIntValue(cJSON_GetObjectItem(json_element, "act_type"));
        dial_element->layer = cJSON_GetIntValue(cJSON_GetObjectItem(json_element, "layer"));

        //element dsc ll
        _lv_ll_init(&dial_element->element_dsc_ll, sizeof(watch_element_base_t *));

        cJSON * json_element_dsc_ll = cJSON_GetObjectItem(json_element, "element_dsc_ll");
        cJSON * json_element_dsc = NULL;
        cJSON_ArrayForEach(json_element_dsc, json_element_dsc_ll) {
            watch_element_base_t ** node = (watch_element_base_t **)_lv_ll_ins_tail(&dial_element->element_dsc_ll);
            watch_element_base_t * dial_element_dsc = lv_mem_alloc(sizeof(watch_element_base_t));
            memset(dial_element_dsc, 0, sizeof(watch_element_base_t));
            *node = dial_element_dsc;

            dial_element_dsc->element_type = cJSON_GetIntValue(cJSON_GetObjectItem(json_element_dsc, "element_type"));
            valuestring = cJSON_GetStringValue(cJSON_GetObjectItem(json_element_dsc, "element_name"));
            if(valuestring) strncpy(dial_element_dsc->element_name, valuestring, WATCH_MAX_DIAL_NAME_LEN);
            valuestring = cJSON_GetStringValue(cJSON_GetObjectItem(json_element_dsc, "element_pre_img"));
            if(valuestring) strncpy(dial_element_dsc->element_pre_img, valuestring, WATCH_MAX_PATH_LEN);
            dial_element_dsc->img_cnt = cJSON_GetIntValue(cJSON_GetObjectItem(json_element_dsc, "img_cnt"));

            //img ll
            _lv_ll_init(&dial_element_dsc->img_ll, sizeof(watch_element_img_dsc_t));

            cJSON * json_img_ll = cJSON_GetObjectItem(json_element_dsc, "img_ll");
            cJSON * json_img = NULL;
            cJSON_ArrayForEach(json_img, json_img_ll) {
                watch_element_img_dsc_t * img_dsc = (watch_element_img_dsc_t *)_lv_ll_ins_tail(&dial_element_dsc->img_ll);
                memset(img_dsc, 0, sizeof(watch_element_img_dsc_t));

                img_dsc->img_type = cJSON_GetIntValue(cJSON_GetObjectItem(json_img, "img_type"));
                json_item = cJSON_GetObjectItem(json_img, "img_pos");
                img_dsc->img_pos.x = cJSON_GetIntValue(cJSON_GetObjectItem(json_item, "x"));
                img_dsc->img_pos.y = cJSON_GetIntValue(cJSON_GetObjectItem(json_item, "y"));

                img_dsc->subimg = lv_mem_alloc(sizeof(watch_element_subimg_dsc_t));
                memset(img_dsc->subimg, 0, sizeof(watch_element_subimg_dsc_t));
                img_dsc->subimg->sub_img_cnt = cJSON_GetIntValue(cJSON_GetObjectItem(json_img, "sub_img_cnt"));

                //sub img ll
                _lv_ll_init(&img_dsc->subimg->sub_img_ll, WATCH_MAX_PATH_LEN);

                cJSON * json_sub_img_ll = cJSON_GetObjectItem(json_img, "sub_img_ll");
                cJSON * json_sub_img = NULL;
                cJSON_ArrayForEach(json_sub_img, json_sub_img_ll) {
                    char * sub_img = (char *)_lv_ll_ins_tail(&img_dsc->subimg->sub_img_ll);
                    memset(sub_img, 0, WATCH_MAX_PATH_LEN);
                    valuestring = cJSON_GetStringValue(cJSON_GetObjectItem(json_sub_img, "sub_img"));
                    if(valuestring) strncpy(sub_img, valuestring, WATCH_MAX_PATH_LEN);
                }
            }

            dial_element_dsc->label_cnt = cJSON_GetIntValue(cJSON_GetObjectItem(json_element_dsc, "label_cnt"));

            //label ll
            _lv_ll_init(&dial_element_dsc->label_ll, sizeof(watch_element_label_dsc_t));

            cJSON * json_label_ll = cJSON_GetObjectItem(json_element_dsc, "label_ll");
            cJSON * json_label = NULL;
            cJSON_ArrayForEach(json_label, json_label_ll) {
                watch_element_label_dsc_t * label_dsc = (watch_element_label_dsc_t *)_lv_ll_ins_tail(&dial_element_dsc->label_ll);
                memset(label_dsc, 0, sizeof(watch_element_label_dsc_t));

                label_dsc->label_type = cJSON_GetIntValue(cJSON_GetObjectItem(json_label, "label_type"));
                valuestring = cJSON_GetStringValue(cJSON_GetObjectItem(json_label, "text"));
                if(valuestring) label_dsc->text = lv_strdup(valuestring);
                json_item = cJSON_GetObjectItem(json_label, "text_color");
                int r = cJSON_GetIntValue(cJSON_GetObjectItem(json_item, "r"));
                int g = cJSON_GetIntValue(cJSON_GetObjectItem(json_item, "g"));
                int b = cJSON_GetIntValue(cJSON_GetObjectItem(json_item, "b"));
                label_dsc->text_color = LV_COLOR_MAKE(r, g, b);
                json_item = cJSON_GetObjectItem(json_label, "text_pos");
                label_dsc->text_pos.x = cJSON_GetIntValue(cJSON_GetObjectItem(json_item, "x"));
                label_dsc->text_pos.y = cJSON_GetIntValue(cJSON_GetObjectItem(json_item, "y"));
                label_dsc->font_size = cJSON_GetIntValue(cJSON_GetObjectItem(json_label, "font_size"));
                valuestring = cJSON_GetStringValue(cJSON_GetObjectItem(json_label, "ttf_file"));
                if(valuestring) strncpy(label_dsc->ttf_file, valuestring, WATCH_MAX_PATH_LEN);
            }

            //bar
            watch_element_bar_dsc_t * bar_dsc = &dial_element_dsc->bar_dsc;
            cJSON * json_bar = cJSON_GetObjectItem(json_element_dsc, "bar");
            bar_dsc->bar_type = cJSON_GetIntValue(cJSON_GetObjectItem(json_bar, "bar_type"));
            json_item = cJSON_GetObjectItem(json_bar, "bar_pos");
            bar_dsc->bar_pos.x = cJSON_GetIntValue(cJSON_GetObjectItem(json_item, "x"));
            bar_dsc->bar_pos.y = cJSON_GetIntValue(cJSON_GetObjectItem(json_item, "y"));
            json_item = cJSON_GetObjectItem(json_bar, "bar_color");
            int r = cJSON_GetIntValue(cJSON_GetObjectItem(json_item, "r"));
            int g = cJSON_GetIntValue(cJSON_GetObjectItem(json_item, "g"));
            int b = cJSON_GetIntValue(cJSON_GetObjectItem(json_item, "b"));
            bar_dsc->bar_color = LV_COLOR_MAKE(r, g, b);
            bar_dsc->bar_w = cJSON_GetIntValue(cJSON_GetObjectItem(json_bar, "bar_w"));
            bar_dsc->bar_h = cJSON_GetIntValue(cJSON_GetObjectItem(json_bar, "bar_h"));
        }
    }

    dial_print(dial);

    cJSON_Delete(json_root);
}

void dial_test(void)
{
    static uint8_t dial_test_add = 0;
    if(dial_test_add) {
        dial_del_dial("领航者", 0);
        dial_del_dial("时空表盘", 0);
        dial_del_dial("摩登电子", 0);
        dial_del_dial("经典机械", 0);
        dial_del_dial("领航者", 1);
        dial_test_add = 0;
        return;
    }

#if LV_WATCH_SIMULATOR_360 != 0
    dial_add_new_dial("C:/machinery.zip");
    dial_add_new_dial("C:/pilot.zip");
    dial_add_new_dial("C:/am_pilot.zip");
    dial_test_add = 1;
#elif LV_WATCH_SIMULATOR != 0
    dial_add_new_dial("C:/digit.zip");
    dial_add_new_dial("C:/space.zip");
    dial_test_add = 1;
#endif
}

#endif /*USE_LV_WATCH_DIAL*/
